using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using eee.Sheffield.PZ.Imaging.ImageFilter;
using eee.Sheffield.PZ.Imaging;
using eee.Sheffield.PZ.Math;

namespace eee.Sheffield.PZ.QAAS
{
    public partial class GVFThinningFilterControl : FilterControl
    {
        #region Fields  
        // gvf thinning
        private Bitmap _srcImage = null;

        private string _tempFolderName = "GVF thinning temp files";

        private string _adaptiveSmoothImageFileName = "adaptive smooth.bmp";
        private Bitmap _adaptiveSmoothImage = null;

        private string _GVFUFileName = "GVF U.txt";
        private string _GVFVFileName = "GVF V.txt";
        private string _GVFVectorMagnitudeFileName = "GVF Vector Magnitude.txt";
        private PZMath_matrix _GVFUMatrix = null;
        private PZMath_matrix _GVFVMatrix = null;
        private PZMath_matrix _GVFVectorMagnitudeMatrix = null;

        private int _sisThreshold;
        private Bitmap _bwImage = null;

        private string _bwCloseImageFileName = "bw close.bmp";
        private Bitmap _bwCloseImage = null;

        private string _thinImageFileName = "thin.bmp";
        private Bitmap _thinImage = null;

        private string _freeEndFileName = "free ends.txt";
        private string _junctionFileName = "junction.txt";
        private List<LineSegment> _lineSegmentList = null;

        private PZMath_matrix _dstMatrix = null;
        private Bitmap _resultImage = null;

        // evaluation
        private string _manualCentreLineImageFileName = "";
        private Bitmap _manualCentreLineImage = null;
        #endregion

        #region Constructors
        public GVFThinningFilterControl()
        {
            InitializeComponent();            
            _flag = "GVF thinning filter";
        }
        public GVFThinningFilterControl(MainForm mainForm, ImageDoc imageDoc)
            : base(mainForm, imageDoc)
        {
            InitializeComponent();
            _flag = "GVF thinning filter";
        }        
        #endregion

        #region controls events
        /// <summary>
        /// on load filter form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GVFThinningFilterControl_Load(object sender, EventArgs e)
        {            
            Reload();
            DetectManualCentreLineImage();
        }

        #region gvf thinning
        /// <summary>
        /// click button Apply
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonApplyMCMC_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;

            if (_srcImage != null)
            {
                Directory.CreateDirectory(_tempFolderName);

                // adaptive smooth
                DetectAdaptiveSmoothImage();

                // GVF
                DetectGVF();

                // SIS threshold
                SISThreshold();

                // closing
                DetectClosing();

                // Peter thinning
                DetectPeterThinning();

                // Split line segments()
                DetectLineSegments();
            }
                       
            // MCMC
            MCMC();            

            ShowImage(_resultImage, "GVF Thinning Result");

            // prepare evaluation
            DetectManualCentreLineImage();

            this.Cursor = Cursors.Arrow;
        }
        #endregion

        #region evaluation
        /// <summary>
        /// click link label "open manual centre line image"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void linkLabelManualCentreLineImage_Click(object sender, EventArgs e)
        {
            if (openFileDialogManualCentreLineImage.ShowDialog() == DialogResult.OK)
            {
                _manualCentreLineImageFileName = openFileDialogManualCentreLineImage.FileName;
            }
            try
            {
                ShowManualCentreLineImage(_manualCentreLineImageFileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Cannot open manual centre line image." + ex.Message);
                _manualCentreLineImageFileName = "";
            }
        }

        /// <summary>
        /// click button "Evaluate"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonEvaluate_Click(object sender, EventArgs e)
        {
            Evaluate(_resultImage, _manualCentreLineImage);
        }        
        #endregion
        #endregion

        #region utility methods
        #region gvf thinning
        /// <summary>
        /// detect adaptive smooth image
        /// </summary>
        private void DetectAdaptiveSmoothImage()
        {
            _adaptiveSmoothImageFileName = Path.Combine(_tempFolderName, _adaptiveSmoothImageFileName);
            if (File.Exists(_adaptiveSmoothImageFileName))
            {
                _adaptiveSmoothImage = new Bitmap(_adaptiveSmoothImageFileName);
                eee.Sheffield.PZ.Imaging.Image.StandardizeImageTo8bbpIndexed(ref _adaptiveSmoothImage);          
            }
            else
            {
                AdaptiveSmooth adaptiveSmoothFilter = new AdaptiveSmooth();
                _adaptiveSmoothImage = adaptiveSmoothFilter.ApplyGraylevelImageFilter(_srcImage);
                _adaptiveSmoothImage.Save(_adaptiveSmoothImageFileName);
            }
            pictureBoxAdaptiveSmooth.Visible = true;
        } // DetectAdaptiveSmoothImage()

        /// <summary>
        /// detect GVF results
        /// </summary>
        private void DetectGVF()
        {
            _GVFUFileName = Path.Combine(_tempFolderName, _GVFUFileName);
            _GVFVFileName = Path.Combine(_tempFolderName, _GVFVFileName);
            _GVFVectorMagnitudeFileName = Path.Combine(_tempFolderName, _GVFVectorMagnitudeFileName);
            if (File.Exists(_GVFUFileName) && File.Exists(_GVFVFileName) && File.Exists(_GVFVectorMagnitudeFileName))
            {
                _GVFVMatrix = new PZMath_matrix(_GVFVFileName);
                _GVFUMatrix = new PZMath_matrix(_GVFUFileName);
                _GVFVectorMagnitudeMatrix = new PZMath_matrix(_GVFVectorMagnitudeFileName);
            }
            else
            {
                double sigma = 1.0;
                double mu = 0.2;
                GradientVectorFlow gvfFilter = new GradientVectorFlow(sigma, mu);
                gvfFilter.ApplyGraylevelImageFilter(_srcImage);
                _GVFVMatrix = gvfFilter.V;
                _GVFUMatrix = gvfFilter.U;
                _GVFVectorMagnitudeMatrix = gvfFilter.VectorMagnitude;
                gvfFilter.WriteFileU(_GVFUFileName);
                gvfFilter.WriteFileV(_GVFVFileName);
                gvfFilter.WriteFileVectorMagnitude(_GVFVectorMagnitudeFileName);
            }
            pictureBoxGVF.Visible = true;
        } // DetectGVF()

        /// <summary>
        /// SIS threshold
        /// </summary>
        private void SISThreshold()
        {
            SISThreshold sisThresholdFilter = new SISThreshold();
            //sisThresholdFilter.LogWriter = logStreamWriter;
            _bwImage = sisThresholdFilter.ApplyGraylevelImageFilter(_adaptiveSmoothImage);
            _sisThreshold = sisThresholdFilter.Threshold;
            //logStreamWriter.WriteLine("SIS threshold DONE.");

            pictureBoxSIS.Visible = true;
        } // SISThreshold()

        /// <summary>
        /// detect closing result
        /// </summary>
        private void DetectClosing()
        {
            _bwCloseImageFileName = Path.Combine(_tempFolderName, _bwCloseImageFileName);
            if (File.Exists(_bwCloseImageFileName))
            {
                _bwCloseImage = new Bitmap(_bwCloseImageFileName);
                eee.Sheffield.PZ.Imaging.Image.StandardizeImageTo8bbpIndexed(ref _bwCloseImage);
            }
            else
            {
                eee.Sheffield.PZ.Imaging.ImageFilter.Closing closeFilter = new Closing();
                _bwCloseImage = closeFilter.ApplyGraylevelImageFilter(_bwImage);                
                _bwCloseImage.Save(_bwCloseImageFileName);
            }

            pictureBoxClosing.Visible = true;
        } // DetectClosing()

        /// <summary>
        /// detect Peter thinning results
        /// </summary>
        private void DetectPeterThinning()
        {
            _thinImageFileName = Path.Combine(_tempFolderName, _thinImageFileName);
            if (File.Exists(_thinImageFileName))
            {
                _thinImage = new Bitmap(_thinImageFileName);
                eee.Sheffield.PZ.Imaging.Image.StandardizeImageTo8bbpIndexed(ref _thinImage);
            }
            else
            {
                RockettThinning thinFilter = new RockettThinning();
                _thinImage = thinFilter.ApplyGraylevelImageFilter(_bwCloseImage);
                _thinImage.Save(_thinImageFileName);
            }

            pictureBoxPeterThinning.Visible = true;
        } // DetectPeterThinning()

        /// <summary>
        /// detect line segements
        /// </summary>
        private void DetectLineSegments()
        {
            _freeEndFileName = Path.Combine(_tempFolderName, _freeEndFileName);
            _junctionFileName = Path.Combine(_tempFolderName, _junctionFileName);

            FreeEndDetector freeEndDetector = new FreeEndDetector();
            if (!File.Exists(_freeEndFileName))
            {
                freeEndDetector.ApplyGraylevelImageFilter(_thinImage);
                freeEndDetector.WriteFile(_freeEndFileName);
            }
            else
            {
                freeEndDetector.ReadFile(_freeEndFileName);
            }
            TripleJunctionDetector junctionDetector = new TripleJunctionDetector();
            if (!File.Exists(_junctionFileName))
            {
                junctionDetector.ApplyGraylevelImageFilter(_thinImage);
                junctionDetector.WriteFile(_junctionFileName);
            }
            else
            {
                junctionDetector.ReadFile(_junctionFileName);
            }

            LineSegmentSplitter splitter = new LineSegmentSplitter(freeEndDetector.FreeEndList, junctionDetector.TripleJunctionList);
            splitter.ApplyGraylevelImageFilter(_thinImage);
            _lineSegmentList = splitter.LineSegmentList;

            pictureBoxSplitLineSegments.Visible = true;
        } // DetectLineSegments()

        /// <summary>
        ///  MCMC method
        /// </summary>
        private void MCMC()
        {
            LineSegmentConfiguration c = new LineSegmentConfiguration(_lineSegmentList);
            c.InitLineSegmentIndex();
            c.AssignDataTerm(_srcImage, _GVFUMatrix, _GVFVMatrix, _GVFVectorMagnitudeMatrix);

            // parameters
            c.W1 = Convert.ToDouble(textBoxW1.Text);       // # of line segments
            c.W2 = Convert.ToDouble(textBoxW2.Text);       // # of free line segments
            c.W3 = Convert.ToDouble(textBoxW3.Text);       // # of free ends
            c.WC = Convert.ToDouble(textBoxWC.Text);     // connection
            c.WL = Convert.ToDouble(textBoxWL.Text);     // length
            c.WG = Convert.ToDouble(textBoxWG.Text);     // Gs value
            c.WI = Convert.ToDouble(textBoxWI.Text);     // Is value

            c.Gt = Convert.ToDouble(textBoxGt.Text);
            if (textBoxIt.Text == "SIS")
                c.It = _sisThreshold;
            else
                c.It = Convert.ToInt16(textBoxIt.Text);
            c.KI = Convert.ToDouble(textBoxKI.Text);

            // prepare MCMC GVF sampler
            int total = Convert.ToInt16(textBoxTotal.Text);
            int burnin = Convert.ToInt16(textBoxBurnIn.Text);
            int space = Convert.ToInt16(textBoxSampleSpace.Text);
            MCMCGVFThinning gvfThinningFilter = new MCMCGVFThinning(_srcImage, _GVFUMatrix, _GVFVMatrix, _GVFVectorMagnitudeMatrix, c, total, burnin, space);
            
            gvfThinningFilter.Sampler.IsCoutinue = checkBoxContinue.Checked;
            gvfThinningFilter.Sampler.MCMCStateFileName = Path.Combine(_tempFolderName, "mcmcState.dat");
            
            // log 
            string logFileName = Path.Combine(_tempFolderName, "log.txt");
            FileStream logFileStream = null; 
            StreamWriter logStreamWriter = null; 
            if (checkBoxLog.Checked)
            {
                logFileStream = new FileStream(logFileName, FileMode.Create, FileAccess.Write);
                logStreamWriter = new StreamWriter(logFileStream);
                gvfThinningFilter.Sampler.LogWriter = logStreamWriter;
            }

            // do MCMC, dstImage is the line segments with high hit probability            
            _dstMatrix = gvfThinningFilter.ApplyDoubleGraylevelImageFilter(new PZMath_matrix(1, 1));

            if (checkBoxLog.Checked)
            {
                logStreamWriter.Flush();
                logStreamWriter.Close();
                logFileStream.Close();
            }

            ConvertBitmapMatrix converter = new ConvertBitmapMatrix();
            Bitmap hittingProbabilityImage = converter.ConvertMatrixToGraylevelBitmap(_dstMatrix, true);
            //logStreamWriter.WriteLine("MCMC DONE.");
            //Console.WriteLine("MCMC DONE.");

            //hittingProbabilityImage.Save(tempFolderName + "//hit.bmp");

            // invert and threshold MCMC result
            string resultImageFileName = Directory.GetCurrentDirectory() + "//result.bmp";

            SISThreshold sisThresholdFilter = new SISThreshold();
            sisThresholdFilter.ApplyGraylevelImageFilter(hittingProbabilityImage);
            int threshold = Convert.ToInt16(sisThresholdFilter.Threshold * 1.5);

            //Console.WriteLine(sisThresholdFilter.Threshold);
            //Console.WriteLine(threshold);

            Threshold thresholdFilter = new Threshold(threshold);
            Bitmap thresholdImage = thresholdFilter.ApplyGraylevelImageFilter(hittingProbabilityImage);

            //thresholdImage.Save("threshold.bmp");

            Negative negativeFilter = new Negative();
            _resultImage = negativeFilter.ApplyGraylevelImageFilter(thresholdImage);
            _resultImage.Save(resultImageFileName);
        } // MCMC()
        #endregion

        #region evaluation
        /// <summary>
        /// detect manual centre line image from current directory
        /// </summary>
        private void DetectManualCentreLineImage()
        {
            string[] candidate = Directory.GetFiles(Directory.GetCurrentDirectory(), "*manual*.bmp");
            if (candidate.Length > 0)
                _manualCentreLineImageFileName = candidate[0];
            try
            {
                ShowManualCentreLineImage(_manualCentreLineImageFileName);
            }
            catch (Exception ex)
            {
                _manualCentreLineImageFileName = "";
            }
        }

        /// <summary>
        /// show manual centre line in picture box
        /// </summary>
        /// <param name="fileName"></param>
        private void ShowManualCentreLineImage(string fileName)
        {
            _manualCentreLineImage = new Bitmap(fileName);
            pictureBoxManualCentreLineImage.Image = _manualCentreLineImage;
            buttonEvaluate.Visible = true;
        }

        /// <summary>
        /// evaluate result image
        /// </summary>
        /// <param name="resultImage"></param>
        /// <param name="manualImage"></param>
        private void Evaluate(Bitmap resultImage, Bitmap manualImage)
        {
            EvaluateThinningByManualSegmentation evaluator = null;
            ImageDoc activeImageDoc = _mainForm._dockPanelMainForm.ActiveDocument as ImageDoc;
            if (activeImageDoc != null)
            {
                evaluator = new EvaluateThinningByManualSegmentation(activeImageDoc.Image, manualImage);
            }
            else
            {
                evaluator = new EvaluateThinningByManualSegmentation(resultImage, manualImage);
            }
            evaluator.EvaluateResult();
            Log(evaluator.FValue);
            Log(evaluator.OValue);
        }
        #endregion

        #endregion
        /// <summary>
        /// Reload()
        /// </summary>
        public override void Reload()
        {
            //buttonApplyMCMC.Enabled = false;

            _tempFolderName = Directory.GetCurrentDirectory() + "//GVF thinning temp files";           
            _adaptiveSmoothImageFileName = "adaptive smooth.bmp";            
            _GVFUFileName = "GVF U.txt";
            _GVFVFileName = "GVF V.txt";
            _GVFVectorMagnitudeFileName = "GVF Vector Magnitude.txt";            
            _bwCloseImageFileName = "bw close.bmp";            
            _thinImageFileName = "thin.bmp";
            _freeEndFileName = "free ends.txt";
            _junctionFileName = "junction.txt";
        
            // prepare gvf thinning
            if (_imageDoc != null)
                _srcImage = (Bitmap)_imageDoc.Image.Clone();            
        } // Reload()

    }
}
